package testChapter
{
	import com.adobe.utils.AGALMiniAssembler;
	import com.adobe.utils.PerspectiveMatrix3D;
	
	import flash.display.Bitmap;
	import flash.display.BitmapData;
	import flash.display.DisplayObject;
	import flash.display.Shape;
	import flash.display.Sprite;
	import flash.display.Stage;
	import flash.display.Stage3D;
	import flash.display.StageAlign;
	import flash.display.StageScaleMode;
	import flash.display3D.*;
	import flash.display3D.textures.Texture;
	import flash.events.Event;
	import flash.events.KeyboardEvent;
	import flash.geom.ColorTransform;
	import flash.geom.Matrix;
	import flash.geom.Matrix3D;
	import flash.geom.Vector3D;
	import flash.text.TextField;
	import flash.text.TextFieldAutoSize;
	import flash.text.TextFormat;
	import flash.utils.getTimer;
	
	import mx.core.mx_internal;
	
	import utils.DisplayMolehill;
	import utils.IRender;

	public class TestChapter extends DisplayMolehill implements IRender
	{
		
		private var fps_last:uint = getTimer();
		private var fps_ticks:uint = 0;
		private var fps_tf:TextField;
		// constants used during inits
		private const swfWidth:int = 600;
		private const swfHeight:int = 400;
		private const textureSize:int = 512;
		
		// the 3d graphics window on the stage
		private var context3D:Context3D;
		// the compiled shader used to render our mesh
		private var shaderProgram:Program3D;
		// the uploaded vertexes used by our mesh
		private var vertexBuffer:VertexBuffer3D;
		// the uploaded indexes of each vertex of the mesh
		private var indexBuffer:IndexBuffer3D;
		// the data that defines our 3d mesh model
		private var meshVertexData:Vector.<Number>;
		// the indexes that define what data is used by each vertex
		private var meshIndexData:Vector.<uint>;
		
		// matrices that affect the mesh location and camera angles
		
		private var projectionMatrix:PerspectiveMatrix3D = new PerspectiveMatrix3D();
		private var modelMatrix:Matrix3D = new Matrix3D();
		private var viewMatrix:Matrix3D = new Matrix3D();
		private var modelViewProjection:Matrix3D = new Matrix3D();
		
		[Embed (source = "assets/texture.jpg")] 
		private var myTextureBitmap:Class;
		private var myTextureData:Bitmap = new myTextureBitmap();
		
		// The Molehill Texture that uses the above myTextureData
		private var myTexture:Texture;
		
		public function TestChapter(stage:Stage)
		{
			super(stage);
			
			initGUI();
			
			stage.stage3Ds[0].addEventListener(Event.CONTEXT3D_CREATE, onContext3DCreate);
			stage.stage3Ds[0].requestContext3D();
			stage.addEventListener(KeyboardEvent.KEY_DOWN,onKey);
		}
		
		private function initGUI():void
		{
			// a text format descriptor used by all gui labels
			var myFormat:TextFormat = new TextFormat();
			myFormat.color = 0xFFFFFF;
			myFormat.size = 13;
			// create an FPSCounter that displays the framerate on screen
			fps_tf = new TextField();
			fps_tf.x = 0;
			fps_tf.y = 0;
			fps_tf.selectable = false;
			fps_tf.autoSize = TextFieldAutoSize.LEFT;
			fps_tf.defaultTextFormat = myFormat;
			fps_tf.text = "Initializing Molehill...";
			stage.addChild(fps_tf);
			
		}
		
		private function initData():void
		{
			meshIndexData = Vector.<uint>
				([
					0, 1, 2, 0, 2, 3,
				]);
			meshVertexData = Vector.<Number>
				( [
					//X, Y, Z, U, V, nX, nY, nZ
					-1, -1, 1, 0, 0, 0, 0, 1,
					1, -1, 1, 1, 0, 0, 0, 1,
					1, 1, 1, 1, 1, 0, 0, 1,
					-1, 1, 1, 0, 1, 0, 0, 1
				]);
		}
		
		private function onContext3DCreate(event:Event):void
		{
			var t:Stage3D = event.target as Stage3D;
			context3D = t.context3D;
			if (context3D == null)
			{
				return;
			}
			trace(context3D.driverInfo);
			context3D.enableErrorChecking = true;
			initData();
			
			context3D.configureBackBuffer(swfWidth, swfHeight, 0, true);
			
			var vertexShaderAssembler:AGALMiniAssembler = new AGALMiniAssembler();
			vertexShaderAssembler.assemble
				(
					Context3DProgramType.VERTEX,
					"m44 op, va0, vc0\n" +
					"mov v0, va0\n" +
					"mov v1, va1\n"
				);
			var fragmentShaderAssembler:AGALMiniAssembler = new AGALMiniAssembler();
			fragmentShaderAssembler.assemble
				(
					Context3DProgramType.FRAGMENT,
					"tex ft0, v1, fs0 <2d,repeat>\n" +
					"mov oc, ft0\n"
				);
			shaderProgram = context3D.createProgram();
			shaderProgram.upload(vertexShaderAssembler.agalcode,fragmentShaderAssembler.agalcode);
			
			indexBuffer = context3D.createIndexBuffer(meshIndexData.length);
			
			indexBuffer.uploadFromVector(meshIndexData, 0, meshIndexData.length);
			vertexBuffer = context3D.createVertexBuffer(meshVertexData.length/8, 8);
			vertexBuffer.uploadFromVector(meshVertexData, 0,meshVertexData.length/8);
			myTexture = context3D.createTexture(textureSize, textureSize,Context3DTextureFormat.BGRA, false);
			
			var newbitmapdata:BitmapData = new BitmapData(512,512,true,0);
			var color:ColorTransform = new ColorTransform(1,1,1,0.2);
			newbitmapdata.draw(myTextureData.bitmapData,null,color);
			
			var newbitmapdata2:BitmapData = new BitmapData(512,512,true,0); 
			var shape:Shape = new Shape;
			shape.graphics.beginFill(0xff0000,0.2);
			shape.graphics.drawRect(0,0,512,512);
			/*shape.graphics.endFill();
			shape.graphics.beginFill(0xff0000,1);
			shape.graphics.drawRect(128,128,256,256);*/
			shape.graphics.endFill();
			//stage.addChild(shape);
			newbitmapdata2.draw(shape);
			
			shape.graphics.clear();
			shape.graphics.beginFill(0x0c0000,0.2);
			shape.graphics.drawRect(0,0,50,50);
			shape.graphics.endFill();
			stage.addChild(shape);
			
			myTexture.uploadFromBitmapData(newbitmapdata2, 0);
			
			var mip:BitmapData = newbitmapdata;
			var texwidth:int = myTextureData.bitmapData.width;
			var n:uint = 0;
			while (texwidth > 1) {
				texwidth /= 2;
				n++;
				var tex2:BitmapData = new BitmapData(texwidth, texwidth, true, 0);
				var mat:Matrix = new Matrix();
				mat.scale(0.5, 0.5);
				tex2.draw(mip, mat, null, null, null, true);
				//myTexture.uploadFromBitmapData(tex2, n);
				mip = tex2;
			}
			
			
			projectionMatrix.identity();
			projectionMatrix.perspectiveFieldOfViewRH(45.0, swfWidth / swfHeight, 0.01, 100.0);
			viewMatrix.identity();
			viewMatrix.appendTranslation(0,0,-4);
			
			context3D.setVertexBufferAt(0, vertexBuffer, 0,Context3DVertexBufferFormat.FLOAT_3);
			context3D.setVertexBufferAt(1, vertexBuffer, 3,Context3DVertexBufferFormat.FLOAT_3);
			
			context3D.setTextureAt(0, myTexture);
			
			context3D.setProgram ( shaderProgram );
			
			//context3D.setCulling(Context3DTriangleFace.FRONT);
			
			context3D.setDepthTest(false,Context3DCompareMode.LESS);
			
			context3D.setBlendFactors(Context3DBlendFactor.SOURCE_COLOR, Context3DBlendFactor.ONE_MINUS_SOURCE_ALPHA);
			
			this.dispatchEvent(new Event(Event.COMPLETE));
		}
		
		private var t:int;
		private var tf:Boolean;
		private function onKey(event:KeyboardEvent):void{
			
			tf = !tf;
			 
		}
		public function render():void{
			context3D.clear(1,1,1);
			
			modelMatrix.identity();
			//modelMatrix.appendScale(0.5*Math.random(),0.5*Math.random(),0.1);
			//modelMatrix.appendTranslation(6*Math.random()-3,2*Math.random()-1,Math.random()-2);//x:-3 3 - 1 1
			
			modelMatrix.appendTranslation(0,0,0);//x:-3 3 - 1 1
			
			modelViewProjection.identity();
			modelViewProjection.append(modelMatrix);
			modelViewProjection.append(viewMatrix);
			modelViewProjection.append(projectionMatrix);
			
			context3D.setProgramConstantsFromMatrix(Context3DProgramType.VERTEX,0, modelViewProjection, true );
			context3D.drawTriangles(indexBuffer, 0, meshIndexData.length/3);
			
			for(var i:int;i<1;i++){
				modelMatrix.identity();
				modelMatrix.appendTranslation(0,0,1);//x:-3 3 - 1 1
				
				modelViewProjection.identity();
				modelViewProjection.append(modelMatrix);
				modelViewProjection.append(viewMatrix);
				modelViewProjection.append(projectionMatrix);
				
				context3D.setProgramConstantsFromMatrix(Context3DProgramType.VERTEX,0, modelViewProjection, true );
				context3D.drawTriangles(indexBuffer, 0, meshIndexData.length/3);
			}
			
			
			
			context3D.present();
			
			
			fps_ticks++;
			var now:uint = getTimer();
			var delta:uint = now - fps_last;
			if (delta >= 1000)
			{
				var fps:Number = fps_ticks / delta * 1000;
				fps_tf.text = fps.toFixed(1) + " fps";
				fps_ticks = 0;
				fps_last = now;
			}
		}
	}
}





/*Sets type of comparison used for depth testing.
	
	The depth of the source pixel output from the pixel shader program is compared to the current value in the depth buffer.
		If the comparison evaluates as false, then the source pixel is discarded. 
			If true, then the source pixel is processed by the next step in the rendering pipeline, the stencil test. 
				In addition, the depth buffer is updated with the depth of the source pixel, as long as the depthMask parameter is set to true.
		
		Sets the test used to compare depth values for source and destination pixels. 
			The source pixel is composited with the destination pixel when the comparison is true. 
				The comparison operator is applied as an infix operator between the source and destination pixel values, in that order.*/


/*Defines the values to use for specifying the source and destination blend factors.
	A blend factor represents a particular four-value vector that is multiplied 
with the source or destination color in the blending formula. The blending formula is:

result = source × sourceFactor + destination × destinationFactor

In the formula, the source color is the output color of the pixel shader program. 
	
	The destination color is the color that currently exists in the color buffer,
as set by previous clear and draw operations.
	
	For example, if the source color is 
(.6, .6, .6, .4) and the source blend factor is Context3DBlendFactor.ONE_MINUS_SOURCE_ALPHA,
	
	then the source part of the blending equation is calculated as:

(.6, .6, .6, .4) × (1-0.4, 1-0.4, 1-0.4, 1-0.4) = (.36, .36, .36, .24)
	
	The final calculation is clamped to the range [0,1].
*/

